import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.border.TitledBorder;

public class ServerGUI extends JFrame implements ActionListener, MyObserver, MyObservable{
	
	JDesktopPane desktop;
	static Server server;
	static Thread startThread;
	private String response;
	public static JTextArea log;
	private static JTextArea options;
	public static ArrayList<String> IPs = new ArrayList<String>();;
	private static String downloadFolder ;
	private static String currentIP;
	MyObserver observer;
	public static JInternalFrame transferFrame;
	MyInternalFrame optFrame, logFrame, passFrame;
	public static List<MyObserver> observers = new ArrayList<MyObserver>();
	public static List<String> previousIPs = new ArrayList<String>();
	public ServerGUI(){
		super("The Open Sync");
		
		int offset = 50;
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(offset, offset,
                  screenSize.width  - offset*2,
                  screenSize.height - offset*2);
        
        desktop = new JDesktopPane(){
        	ImageIcon backImage = new ImageIcon("background.png");
            Image image = backImage.getImage();
            {setOpaque(false);}
            //painting desktop background image:
            public void paintComponent (Graphics g) {
              g.drawImage(image, 0, 0, super.getWidth() - 300 , super.getHeight() ,this);
              super.paintComponent(g);
            }
        };
        
        //creating transfer icon:
        createTransferFrame();
        
        //createFrames();
        createLogFrame();
        createOptionsFrame();
        createPasswordFrame();
        
        setContentPane(desktop);
        setJMenuBar(createMenuBar());
        
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
        
	}

	public JMenuBar createMenuBar(){
		JMenuBar menuBar = new JMenuBar();
		//file menu:
		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);
		menuBar.add(fileMenu);
		//create open menu item and attach it:
		JMenuItem openItem = new JMenuItem("Open");
		openItem.setMnemonic(KeyEvent.VK_O);
		openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,ActionEvent.CTRL_MASK));
		openItem.setActionCommand("open");
		openItem.addActionListener(this);
		fileMenu.add(openItem);
		//create exit menu item and attach it:
		JMenuItem exitItem = new JMenuItem("Exit");
		exitItem.setMnemonic(KeyEvent.VK_X);
		exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK));
		exitItem.setActionCommand("exit");
		exitItem.addActionListener(this);
		fileMenu.add(exitItem);
		//window menu:
		JMenu windowMenu = new JMenu("Window");
		windowMenu.setMnemonic(KeyEvent.VK_W);
		menuBar.add(windowMenu);
		//create show menu and attach it:
		JMenu showItem = new JMenu("Show View");
		showItem.setMnemonic(KeyEvent.VK_V);
		windowMenu.add(showItem);
		//create log option for show menu item and attach it:
		JMenuItem logItem = new JMenuItem("Log");
		logItem.setActionCommand("logging");
		logItem.addActionListener(this);
		showItem.add(logItem);
		//create options for show menu item and attach it:
		JMenuItem optionsItem = new JMenuItem("Options");
		optionsItem.setActionCommand("options");
		optionsItem.addActionListener(this);
		showItem.add(optionsItem);
		
		return menuBar;
	}
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		// TODO Auto-generated method stub
		if ("exit".equals(arg0.getActionCommand())) {
            quit();
        }
		if ("open".equals(arg0.getActionCommand())) {
			open();
		}
		if ("logging".equals(arg0.getActionCommand())) {
			logFrame.setVisible(true);
			try{
				logFrame.setSelected(true);
			} catch(java.beans.PropertyVetoException e){}
		}
		if ("options".equals(arg0.getActionCommand())) {
			optFrame.setVisible(true);
			try{
				optFrame.setSelected(true);
			} catch(java.beans.PropertyVetoException e){}
		}
	}
	
	public void createTransferFrame(){
		//build the transfer frame and attach it to desktop:
		transferFrame = new JInternalFrame();
		transferFrame.setSize(400,170);
		transferFrame.setLocation(400,200);
		desktop.add(transferFrame);
		try {
			transferFrame.setSelected(true);
		} catch (PropertyVetoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		JTextArea transfer = new JTextArea(){
        	ImageIcon backImage = new ImageIcon("transfer.jpg");
            Image image = backImage.getImage();
            {setOpaque(false);}
            public void paintComponent (Graphics g) {
              g.drawImage(image, 0, 0, 400 , 150 ,this);
              //g.drawImage(image, 100, 100, null, this);
              super.paintComponent(g);
            }
        };
        transfer.setVisible(true);
        transfer.setEditable(false);
        transferFrame.add(transfer);
		
	}
	
	public void createLogFrame(){
		//build the log frame and attach it to desktop:
		logFrame = new MyInternalFrame("Log");
		
		logFrame.setVisible(true);
        desktop.add(logFrame);
        try {
        	logFrame.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
        //adding logger and scroll pane:
        log = new JTextArea(logFrame.getHeight()-10,logFrame.getWidth()-10);
        log.setMargin(new Insets(5,5,5,5));
        JScrollPane scroll = new JScrollPane(log);
        scroll.setSize(600,600);
        scroll.setVisible(true);
        
        log.setEditable(false);
        log.setText("Server started.\nWaiting for connections...");
        
        logFrame.setContentPane(scroll);
        scroll.setViewportView(log);
        
        logFrame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
	}
	
	
	public void createOptionsFrame(){
		//build the options frame and attach it to desktop:
        optFrame = new MyInternalFrame("Options");
        optFrame.setLocation(950, 0);
        optFrame.setVisible(true);
        desktop.add(optFrame);
        try{
        	optFrame.setSelected(true);
        } catch (java.beans.PropertyVetoException e){}
        //adding fields to options frame:
        String [] allAddrs = null;
        try{
        	InetAddress   in  = InetAddress.getLocalHost();  
    		InetAddress[] all = InetAddress.getAllByName(in.getHostName());  
    		allAddrs = new String[all.length];
    		for (int i=0; i<all.length; i++) {  
    			allAddrs[i] = all[i].getHostAddress();
    		}
        } catch(UnknownHostException e){
        	e.printStackTrace();
        }
        
        JComboBox comboBox = new JComboBox();
        for (int i = 0; i < allAddrs.length; i++){
        	comboBox.addItem(allAddrs[i]);
        }
        //comboBox.setSelectedItem(allAddrs[2]);
        comboBox.addActionListener(new java.awt.event.ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				comboBoxActionPerformed(arg0);
			}
        });
        
        optFrame.getContentPane().setLayout(new FlowLayout());
        //optFrame.getContentPane().add(new ComboPanel("    IP Address   ", new ComboPanel(currentIP,comboBox)));
        optFrame.getContentPane().add(new ComboPanel("    IP Address   ",comboBox));
        
        JButton downButton = new JButton();
        downButton.setText("   Change   ");
        downButton.addActionListener(new java.awt.event.ActionListener(){

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				downButtonActionPerformed(e);
				// TODO Repaint the optFrame:
				//repaint(optFrame, )
			}
        });
        
        optFrame.getContentPane().add(new ComboPanel("  Download folder  ", downButton));
        //optFrame.getContentPane().add(new ComboPanel("  Download folder  ", new ComboPanel(downloadFolder, downButton)));
        
        JButton passButton = new JButton();
        passButton.setText("  Change  ");
        passButton.addActionListener(new java.awt.event.ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				passwordChange();
			}
        });
        
        optFrame.getContentPane().add(new ComboPanel(" Password ", passButton));
        
        optFrame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
        
        JTextArea warning = new JTextArea();
        warning.setEditable(false);
        warning.setText("Please use the\nchange IP functionality ONLY\n" +
        				"if the server has not started\nwith the IP Address of your machine\n");
        warning.setVisible(true);
        optFrame.getContentPane().add(new ComboPanel("",warning));
        
    }
	
	public void downButtonActionPerformed(ActionEvent evt){
		JFileChooser fileChooser = new JFileChooser();
		fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		fileChooser.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
		fileChooser.setDoubleBuffered(true);
		fileChooser.setDragEnabled(true);
		fileChooser.setDialogTitle("Select new download folder");
		
		File crt = null;
		
		if (downloadFolder.equals("")) {
			//first change of download folder:
			try {
				crt = new File(new File(".").getCanonicalPath());
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		else{
			//download folder previously changed:
			crt = new File(downloadFolder);
			
		}
		fileChooser.setCurrentDirectory(crt);
		
		int offset = 50;
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(offset, offset,
                  screenSize.width  - offset*2,
                  screenSize.height - offset*2);
        
        int returnValue = fileChooser.showOpenDialog(ServerGUI.this);
        
        if (returnValue == JFileChooser.APPROVE_OPTION){
        	File fc = fileChooser.getSelectedFile();
        	downloadFolder = fc.toString();
        	//state(i.e. download folder) has changed; notifying the server:
        	this.notifyObservers();
        }
	}
	
	public void createPasswordFrame(){
		passFrame = new MyInternalFrame("Change password");
		passFrame.setLocation(450, 150);
		passFrame.setSize(passFrame.getWidth(), 230);
		//passFrame.setVisible(true);
		desktop.add(passFrame);
		passFrame.setLayout(new FlowLayout());
		
	}
	public void passwordChange(){
		passFrame.setVisible(true);
		try {
			passFrame.setSelected(true);
		} catch (PropertyVetoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//type password:
		final JPasswordField pass1 = new JPasswordField(25);
		passFrame.getContentPane().add(new ComboPanel("  Type password   ",pass1));
		//re-type password:
		final JPasswordField pass2 = new JPasswordField(25);
		passFrame.getContentPane().add(new ComboPanel(" Re-type password ",pass2));
		//Ok and Cancel buttons:
		JButton ok = new JButton("OK");
		JButton cancel = new JButton("Cancel");
		
		passFrame.getContentPane().add(ok);
		passFrame.getContentPane().add(cancel);
		
		//log, just in case user doesn't re-type the same password:
		final JTextArea passLog = new JTextArea();
		passLog.setVisible(true);
		passLog.setEditable(false);
		passLog.setRows(2);
		passLog.setColumns(20);
		
		passFrame.getContentPane().add(passLog);
		
		ok.addActionListener(new java.awt.event.ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				String pass1Text = new String(pass1.getPassword());
				String pass2Text = new String(pass2.getPassword());
				if (pass1Text.equals(pass2Text)){
					//server.mdchecker.addPassword(pass1Text);
					for(ServiceThread st : server.serviceThreadList){
						st.mdchecker.addPassword(pass1Text);
					}
					passFrame.setVisible(false);
					pass1.setEditable(false);
					pass2.setEditable(false);
				}
				else{
					passLog.setText(passLog.getText()+"\nPlease re-type the same password");
				}
			}
		});
	}
	
	class ComboPanel extends JPanel{
		ComboPanel(String title, JComponent c){
			setLayout(new FlowLayout());
			setBorder(new TitledBorder(title));
			add(c);
		}
	}
	
	public void quit(){
		System.exit(0);
	}
	
	public void comboBoxActionPerformed(java.awt.event.ActionEvent evt){
		JComboBox comboBox = (JComboBox) evt.getSource();
		String selectedIP = comboBox.getSelectedItem().toString();
		
		
		if (currentIP.equals(selectedIP) == false && previousIPs.contains(selectedIP) == false){
			currentIP = selectedIP;
			previousIPs.add(currentIP);
			
			int noIPs = comboBox.getItemCount();
			int foundIndex = 0;
			String [] ipVect = new String[noIPs];
			for (int i = 0 ; i < noIPs; i ++){
				ipVect[i] = comboBox.getItemAt(i).toString();
				if (ipVect[i].equals(currentIP)){
					foundIndex = i;
				}
			}
			
			//interrupting the server thread:
			server.interrupt();
			
			//restarting the server:
			System.out.println("Restarting the server...");
			
			server = new Server(currentIP);
			try{
				server.join();
			}catch(Exception e){
				System.out.println(e.getMessage());
			}
		}
		else{
			//DO NOTHING, because you can't bind the server socket to the same IP
		}
		
		
		/*
		comboBox.removeItemAt(foundIndex);
		//re-adding all IPs except the previously selected one:
		
		for (int i = 0 ; i < noIPs; i++){
			if(ipVect[i].equals(currentIP) == false){
				comboBox.addItem(ipVect[i]);
			}
		}
		*/
		//System.out.println("this download folder:"+server.downloadFolder);
	}
	
	public void open(){
		JFileChooser fileChooser = new JFileChooser();
		fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		fileChooser.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
		fileChooser.setDoubleBuffered(true);
		fileChooser.setDragEnabled(true);
		
		File crt =null;
		if(this.downloadFolder.equals("") == false){
			crt = new File(this.downloadFolder);
		}
		else{
			try {
				crt = new File(new File(".").getCanonicalPath());
				
			} catch (IOException e) {
				//e.printStackTrace();
				crt = null;
			}
		}
		fileChooser.setCurrentDirectory(crt);
		
		int offset = 50;
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(offset, offset,
                  screenSize.width  - offset*2,
                  screenSize.height - offset*2);
        
        int returnValue = fileChooser.showOpenDialog(ServerGUI.this);
        
        if (returnValue == JFileChooser.APPROVE_OPTION){
        	File fc = fileChooser.getSelectedFile();
        	try {
				Process p = Runtime.getRuntime().exec("explorer "+fc.toString());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
	}
	
	public static void setGUIlookAndFeel(){
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedLookAndFeelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*
		ServerGUI GUI = new ServerGUI();
		GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		GUI.setVisible(true);
		*/
	}
	
	@Override
	public void update(MyObservable o, Object arg) {
		// TODO Auto-generated method stub
		
		if(arg instanceof String){
			response = (String) arg;
			log.setText(log.getText()+"\n"+response);
		}
		if(arg instanceof Integer){
			int sending = ((Integer) arg).intValue();
			if ( sending == 1 )
				transferFrame.setVisible(true);
			else 
				if (sending == 0)
					transferFrame.setVisible(false);
		}
	}
	
	public static void main(String[] args){
		//setting look and feel:
		setGUIlookAndFeel();
		//getting current system path:
		try {
			downloadFolder = (new File(".")).getCanonicalPath();
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		//getting IP addresses:
		try {
			 InetAddress   in  = InetAddress.getLocalHost();  
			 InetAddress[] all = InetAddress.getAllByName(in.getHostName());  
			 for (int i=0; i<all.length; i++) {  
			     System.out.println(i + ".  address = " + all[i].getHostAddress()/*+"  "+ all[i].isReachable(1000)*/);
				 
			     IPs.add(all[i].getHostAddress());
			 }
			 
			 try {
				ArrayList<NetworkInterface> netInterfs =  Collections.list(NetworkInterface.getNetworkInterfaces());
				for (NetworkInterface ni : netInterfs){
					//System.out.println(ni.getName()+ "  " + ni.isUp() );
				}
			} catch (SocketException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (UnknownHostException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		currentIP = IPs.get(2);
		
		//starting GUI:
		ServerGUI GUI = new ServerGUI();
		GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		GUI.setVisible(true);
		
		//starting server:
		server = new Server(currentIP);
		server.start();
		
		previousIPs.add(currentIP);
		
		//adding observers:
		GUI.addObserver(server);
		
		server.addObserver(GUI);
		
		for (ServiceThread st : server.serviceThreadList){
			st.addObserver(GUI);
		}
		
		GUI.notifyObservers();
		
		try{
			server.join();
		}catch(Exception e){
			System.out.println(e.getMessage());
		}
		
		for (ServiceThread st : server.serviceThreadList){
			try{
				st.join();
			} catch(Exception e){
				System.out.println("Error in thread join");
			}
		}
	}

	@Override
	public void addObserver(MyObserver o) {
		// TODO Auto-generated method stub
		
		observers.add(o);
	}

	@Override
	public void notifyObservers() {
		// TODO Auto-generated method stub
		//observer.update(this, downloadFolder);
		for (MyObserver obs : observers){
			obs.update(this, downloadFolder);
		}
	}

	@Override
	public void notifyObservers(Object o) {
		// TODO Auto-generated method stub
		//Nothing to be done here
	}
}